In this notebook we will add TP53 alteration status as discussed in #837:
TP53 altered - loss, if :
- A sample contains a TP53 hotspot SNV mutation. (Cancer hotspot database and downloadable file available). Please also crosscheck that all mutations from this table are included.
- A sample contains two TP53 alterations, suggesting (but not confirming) that both alleles are affected (SNV+SNV, (CNV or SV) + SNV).
- A sample contains one alteration (SNV or (CNV or SV)) + has cancer_predispositions == “Li-Fraumeni syndrome”, suggesting there is a germline variant in addition to the somatic variant we observe.
- A sample does not have a TP53 alterations, but has cancer_predispositions == “Li-Fraumeni syndrome” and TP53 classifier score for matched RNA-Seq > 0.5 (or higher cutoff we decide upon later).
Note: CNV and SV will be considered as the same event, but we will be adding SV_counts and SV.type to the altered status output file
TP53 altered - activated, if:
- A sample contains one of the two TP53 activating mutations p.R273C and p.R248W. [@doi:10.1038/ng0593-42]
Setup
library("ggpubr")
Loading required package: ggplot2
Loading required package: magrittr
library("ggthemes")
library("tidyverse")
── Attaching packages ────────────────────────────────── tidyverse 1.2.1 ──
✔ tibble 2.1.3 ✔ purrr 0.3.2
✔ tidyr 0.8.3 ✔ dplyr 0.8.3
✔ readr 1.3.1 ✔ stringr 1.4.0
✔ tibble 2.1.3 ✔ forcats 0.4.0
── Conflicts ───────────────────────────────────── tidyverse_conflicts() ──
✖ tidyr::extract() masks magrittr::extract()
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
✖ purrr::set_names() masks magrittr::set_names()
library("ggfortify")
library("broom")
# rootdir
root_dir <- rprojroot::find_root(rprojroot::has_dir(".git"))
data_dir <- file.path(root_dir, "data")
input_dir <- file.path(root_dir,
"analyses",
"tp53_nf1_score",
"input")
# cell composition
cell_line_composition <- read_tsv(file.path("..",
"molecular-subtyping-HGG",
"input",
"cell-line-composition.tsv"),
col_types =
readr::cols( aliquot_id = readr::col_character()))
# histology
if ( params$base_run ==0 ){
clinical<-read.delim(file.path(data_dir,"histologies.tsv"), stringsAsFactors = FALSE)
} else{
clinical<-read.delim(file.path(data_dir,"histologies-base.tsv"), stringsAsFactors = FALSE)
}
# recording of "BS_ETC8R0TD" GMKF NBL sample; needs to be remove once updated in v12
#clinical$RNA_library[clinical$Kids_First_Biospecimen_ID == "BS_ETC8R0TD"] <- "stranded"
histology <- clinical %>%
dplyr::select("Kids_First_Biospecimen_ID",
"sample_id",
"Kids_First_Participant_ID",
"cancer_predispositions",
"sample_type",
"experimental_strategy",
"composition",
"cohort") %>%
# merge cell line composition
left_join(cell_line_composition)
Joining, by = c("Kids_First_Biospecimen_ID", "sample_id", "Kids_First_Participant_ID")
# Cancer database
hotspot_database_2017_tp53_snv <- readxl::read_xls(file.path(input_dir,"hotspots_v2.xls"),sheet = 1) %>%
filter(Hugo_Symbol == "TP53")
hotspot_database_2017_tp53_indel <- readxl::read_xls(file.path(input_dir,"hotspots_v2.xls"),sheet = 2) %>%
filter(Hugo_Symbol == "TP53")
# p53, p63 and p73 functional sites
functional_sites_tp53 <- read_tsv(file.path(input_dir,"hotspot_Chen_2006.tsv"))
Parsed with column specification:
cols(
p53 = col_character(),
p63 = col_character(),
p73 = col_character()
)
results_dir <- file.path(root_dir,
"analyses",
"tp53_nf1_score",
"results")
if (!dir.exists(results_dir)) {
dir.create(results_dir)
}
# Read in combined scores from tp53-nf1-classifier
classifier_score <- read_tsv(file.path(results_dir, "combined_scores.tsv")) %>%
dplyr::rename(Kids_First_Biospecimen_ID_RNA = sample_id) %>%
filter(Kids_First_Biospecimen_ID_RNA %in% histology$Kids_First_Biospecimen_ID) %>%
left_join(histology, by=c("Kids_First_Biospecimen_ID_RNA"="Kids_First_Biospecimen_ID"))
Parsed with column specification:
cols(
sample_id = col_character(),
tp53_score = col_double(),
tp53_shuffle = col_double()
)
# Copy number per sample
# overlapping functional domain
cnv_domain_overlap <- read_tsv(
file.path(
results_dir,
"loss_overlap_domains_tp53.tsv"))
Parsed with column specification:
cols(
biospecimen_id = col_character(),
copy_number = col_double(),
ploidy = col_double(),
domain = col_character(),
Kids_First_Participant_ID = col_character(),
sample_id = col_character(),
composition = col_character(),
tumor_descriptor = col_character(),
tp53_shuffle = col_double()
)
# Structural variant overlapping
# or within gene locus of TP53
sv_overlap <- read_tsv(
file.path(
results_dir,
"sv_overlap_tp53.tsv"))
Parsed with column specification:
cols(
SV_chrom = col_double(),
SV_start = col_double(),
SV_end = col_double(),
SV_length = col_double(),
Kids.First.Biospecimen.ID.Tumor = col_character(),
SV_type = col_character(),
Gene_name = col_character(),
ALT = col_character(),
sample_id = col_character()
)
# Structural variant as a fusion in exon1/intron1
fusion_overlap <- read_tsv(
file.path(
results_dir,
"fusion_bk_tp53_loss.tsv"))
Parsed with column specification:
cols(
seqnames = col_character(),
start = col_double(),
end = col_double(),
width = col_double(),
strand = col_character(),
Sample = col_character(),
sample_id = col_character(),
FusionName = col_character(),
variable = col_character(),
value = col_time(format = "")
)
Warning: 94 parsing failures.
row col expected actual file
1 value valid date 17:7685631 '/home/rstudio/OpenPedCan-analysis/analyses/tp53_nf1_score/results/fusion_bk_tp53_loss.tsv'
2 value valid date 17:7687377 '/home/rstudio/OpenPedCan-analysis/analyses/tp53_nf1_score/results/fusion_bk_tp53_loss.tsv'
3 value valid date 17:7687377 '/home/rstudio/OpenPedCan-analysis/analyses/tp53_nf1_score/results/fusion_bk_tp53_loss.tsv'
4 value valid date 17:7687377 '/home/rstudio/OpenPedCan-analysis/analyses/tp53_nf1_score/results/fusion_bk_tp53_loss.tsv'
5 value valid date 17:7687377 '/home/rstudio/OpenPedCan-analysis/analyses/tp53_nf1_score/results/fusion_bk_tp53_loss.tsv'
... ..... .......... .......... ...........................................................................................
See problems(...) for more details.
Check if all function sites are in hotspots
All functional sites are just 1 base so checking in hotspot_database_2017_snv
functional_sites_tp53 %>%
filter(!gsub("[A-Z|a-z]","",p53) %in% hotspot_database_2017_tp53_snv$Amino_Acid_Position )
2 functional sites are missing in hotspots database.
SNV for TP53
Removing Silent or Intron classified variants to capture only putative damaging mutations
consensus_tp53_snv_indel <- data.table::fread(
file.path(data_dir,"snv-consensus-plus-hotspots.maf.tsv.gz"),
select = c("Chromosome",
"Start_Position",
"End_Position",
"Strand",
"Variant_Classification",
"Tumor_Sample_Barcode",
"Hugo_Symbol",
"HGVSp_Short"),
data.table = FALSE) %>%
filter(Hugo_Symbol == "TP53") %>%
filter(!(Variant_Classification %in% c("Silent", "Intron",
# remove other non-amino acid SNVs
"3'Flank" ,"5'Flank",
"3'UTR", "5'UTR" )))
Registered S3 method overwritten by 'R.oo':
method from
throw.default R.methodsS3
Gather annotation for SNV
hotspots : overlaps AA position which are statistically significant SNV/Indels activating : overlaps AA position which are found to act as gain-of-function according to literature
consensus_tp53_snv_indel <- consensus_tp53_snv_indel %>%
mutate(
hotspot = case_when(
# strip REF and Variant AA to get Amino_Acid_Position in consensus maf
(gsub("[A-Z|a-z]|[.]","",HGVSp_Short) %in%
# if overlaps the hotspot Amino_Acid_Position
hotspot_database_2017_tp53_snv$Amino_Acid_Position) ~ 1,
TRUE ~ 0),
activating = case_when(
# strip REF and Variant AA to get Amino_Acid_Position in consensus maf
(gsub("[A-Z|a-z]|[.]","",HGVSp_Short) %in%
# if overlaps the activating Amino_Acid_Position
c("273","248")) ~ 1,
TRUE ~ 0
)
)
Gather SNV,CNV, classifier and cancer_predisposition values
We will gather values per sample_id since DNA and RNA samples can be only matched by sample_id
tp53_alterations <- histology %>%
filter(experimental_strategy != "RNA-Seq") %>%
# filter for Tumors
filter(sample_type=="Tumor") %>%
# join consensus calls overlapping hotspots
left_join(consensus_tp53_snv_indel,
by=c("Kids_First_Biospecimen_ID"="Tumor_Sample_Barcode")) %>%
# join filtered cnv losses
left_join(cnv_domain_overlap,by=c("Kids_First_Biospecimen_ID"="biospecimen_id",
"sample_id", "Kids_First_Participant_ID",
"composition")) %>%
# join SV
left_join(sv_overlap,by=c("Kids_First_Biospecimen_ID"="Kids.First.Biospecimen.ID.Tumor",
"sample_id")) %>%
dplyr::rename(Kids_First_Biospecimen_ID_DNA = Kids_First_Biospecimen_ID) %>%
# join fusion
left_join(fusion_overlap,by="sample_id")
tp53_alterations_score <- tp53_alterations %>%
# add classifier score
full_join(classifier_score,by=c("sample_id",
"composition",
"cell_line_composition",
"cancer_predispositions",
"sample_type",
"Kids_First_Participant_ID",
"cohort")) %>%
# select useful columns
dplyr::select(sample_id,Kids_First_Biospecimen_ID_RNA,Kids_First_Biospecimen_ID_DNA,
tp53_score,cancer_predispositions,HGVSp_Short,copy_number,SV_type,FusionName,hotspot,activating) %>%
arrange(sample_id) %>%
unique() %>%
replace_na(list(hotspot = 0, activating = 0))
tp53_alterations_score
Convert the above to wide format
tp53_alterations_score_wide <- tp53_alterations_score %>%
# group
group_by(sample_id,
Kids_First_Biospecimen_ID_DNA,
Kids_First_Biospecimen_ID_RNA,
cancer_predispositions,
tp53_score) %>%
#
# sample_id as rows add SNV counts,CNV loss counts
# and hotspot/activating mutation annotation
summarise(
# summarize length of SNV and CNV alterations
SNV_indel_counts= length(unique(HGVSp_Short[!is.na(HGVSp_Short)])),
CNV_loss_counts = length(unique(copy_number[!is.na(copy_number)])),
SV_counts = length(unique(SV_type[!is.na(SV_type)])),
Fusion_counts = length(unique(FusionName[!is.na(FusionName)])),
HGVSp_Short = toString(unique(HGVSp_Short)),
CNV_loss_evidence = toString(unique(copy_number)),
SV_type = toString(unique(SV_type)),
Fusion_evidence = toString(unique(FusionName)),
# summarize unique hotspot values per sample_id
hotspot = max(unique(hotspot[!is.na(hotspot) ])),
activating = max(unique(activating[!is.na(activating)])))
tp53_alterations_score_wide
Add annotation
As discussed above we want to annotate TP53 mutants that are putative loss-of-function OR gain-of-function.
tp53_alterations_score_wide <- tp53_alterations_score_wide %>%
mutate(
# add tp53_altered annotation column
tp53_altered =
case_when(
# when activating == 0
activating == 0 &
# check if mutated variant AA position overlaps hotspot database
( hotspot == 1 |
# check if sample_id has SNV+(CNV|SV) mutation
# suggesting both alleles are mutated
(SNV_indel_counts >= 1 &
(CNV_loss_counts >=1 | SV_counts >=1) ) |
# check if more than 1 SNV is present
# suggesting both alleles are mutated
SNV_indel_counts > 1 |
# check if SNV mutant and has
# Li-Fraumeni syndrome suggesting
# germline TP53 mutant as cancer predisposition
(SNV_indel_counts >= 1 &
grepl("Li-Fraumeni syndrome",cancer_predispositions)) |
# check if CNV|SV mutant and has
# Li-Fraumeni syndrome suggesting
# germline TP53 mutant as cancer predisposition
((CNV_loss_counts >= 1 | SV_counts >=1 ) &
grepl("Li-Fraumeni syndrome",cancer_predispositions)) |
# check if tp53 inactivating score for RNA_Seq is greater that 0.5
# and has Li-Fraumeni syndrome suggesting
# germline TP53 mutant as cancer predisposition
(tp53_score > 0.5 &
grepl("Li-Fraumeni syndrome",cancer_predispositions)) |
# check if tp53 inactivating score for RNA_Seq is greater that 0.5
# and has 1 or more SNV| (CNV|SV) loss in TP53
(tp53_score > 0.5 &
(SNV_indel_counts >= 1 | (CNV_loss_counts >=1 | SV_counts >=1 )))
) ~ "loss",
# when activating == 1
activating == 1 ~ "activated",
# if no evidence supports TP53 inativation or activation
TRUE ~ "Other"
)
)
Explore distribution of tp53_altered status vs tp53 inactivation scores
ggplot(tp53_alterations_score_wide, aes(x = factor(tp53_altered), y = tp53_score)) +
geom_violin()+
geom_jitter(alpha = 0.5, width = 0.2) +
stat_compare_means() +
theme_bw() +
ggtitle("Distribution of scores across tp53 altered status") +
theme(axis.text.x = element_text(angle = 60, hjust = 1))+
xlab("tp53 altered status")
Warning: Removed 3974 rows containing non-finite values (stat_ydensity).
Warning: Removed 3974 rows containing non-finite values
(stat_compare_means).
Warning: Removed 3974 rows containing missing values (geom_point).

TP53 mutants with gain-of-function (activating) mutants promote tumorigenesis according to literature. Reference and reference have similar distribution of classifier scores to that of potential loss-of-function (multi-allelic) Tp53 mutations.
“Other” annotated samples either don’t have any high-confidence loss/gain TP53 SNVs nor CNV losses OR DNA sample is not available.
Expression profile for activating vs loss TP53 status
expr_combined <- readRDS(file.path(data_dir,"gene-counts-rsem-expected_count-collapsed.rds"))
Expression profile for activating vs loss TP53 status
# iterate through all possible RNA library type
# first filter to
clinical_filtered <- clinical %>%
filter(Kids_First_Biospecimen_ID %in% names(expr_combined))
# generate list of all RNA library type
rna_library_list <- clinical_filtered %>%
filter(Kids_First_Biospecimen_ID %in% names(expr_combined)) %>%
pull(RNA_library) %>%
unique()
for(i in 1:length(rna_library_list)) {
library_each <- rna_library_list[[i]]
# pull BS IDs for that RNA library type
library_samples <- clinical_filtered %>%
filter(RNA_library == library_each) %>%
pull(Kids_First_Biospecimen_ID) %>%
unique()
# subset expression matrix using that
expr_each <- expr_combined %>%
dplyr::select(library_samples)
# subset to TP53
subset_expr_each <- t(expr_each)[,"TP53"] %>%
as.data.frame()
colnames(subset_expr_each) <- "TP53"
# combine the TP53 alteration information
subset_expr_combined <- subset_expr_each %>%
tibble::rownames_to_column() %>%
left_join(tp53_alterations_score_wide,by=c("rowname"="Kids_First_Biospecimen_ID_RNA")) %>%
filter(tp53_altered %in% c("activated","loss"))
# plot distribution of TP53 gene expression
plot<- ggplot(subset_expr_combined, aes(x = factor(tp53_altered), y = TP53)) +
geom_violin()+
geom_jitter(alpha = 0.5, width = 0.2) +
stat_compare_means() +
theme_bw() +
ggtitle("Distribution of TP53 expression across tp53 altered status") +
theme(axis.text.x = element_text(angle = 60, hjust = 1))+
xlab(paste0("tp53 altered status wth RNA library ", library_each))
print(plot)
}




Check if other cancer predisposition have high TP53 inactivation scores
ggplot(tp53_alterations_score_wide, aes(x = factor(tp53_altered), y = tp53_score)) +
geom_violin()+
geom_jitter(alpha = 0.5, width = 0.2) +
theme_bw() +
ggtitle("Distribution of scores across tp53 altered status") +
theme(axis.text.x = element_text(angle = 60, hjust = 1))+
xlab("tp53 altered status") +
facet_wrap(.~cancer_predispositions)
Warning: Removed 3974 rows containing non-finite values (stat_ydensity).
Warning in max(data$density): no non-missing arguments to max; returning -
Inf
Warning in max(data$density): no non-missing arguments to max; returning -
Inf
Warning in max(data$density): no non-missing arguments to max; returning -
Inf
Warning: Removed 3974 rows containing missing values (geom_point).

Some NF-1 and/or Other inherited conditions NOS have high scoring TP53 mutants as well.
Interesting 2 bs ids annotated with Li-Fraumeni synfrome pre-disposition have very low tp53 classifier scores.
tp53_alterations_score_wide %>%
filter(grepl("Li-Fraumeni syndrome", cancer_predispositions),
tp53_score < 0.5)
Save file
Adding DNA and RNA biospecimen ids to tp53_alterations_score_wide and saving
tp53_alterations_score_wide %>%
write_tsv(file.path(results_dir,"tp53_altered_status.tsv"))
LS0tCnRpdGxlOiAiVHA1MyBTTlYgaG90c3BvdHMiCmF1dGhvcjogIksgUyBHYW9ua2FyIChEM0IpIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKcGFyYW1zOgogIGJhc2VfcnVuOgogICAgbGFiZWw6ICIxLzAgdG8gcnVuIHdpdGggYmFzZSBoaXN0b2xvZ3kiCiAgICB2YWx1ZTogMAogICAgaW5wdXQ6IGludGVnZXIKLS0tCgpJbiB0aGlzIG5vdGVib29rIHdlIHdpbGwgYWRkIFRQNTMgYWx0ZXJhdGlvbiBzdGF0dXMgYXMgZGlzY3Vzc2VkIGluIFsjODM3XShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS9PcGVuUEJUQS1hbmFseXNpcy9pc3N1ZXMvODM3KToKCioqVFA1MyBhbHRlcmVkIC0gbG9zcyoqLCBpZiA6CgogLSBBIHNhbXBsZSBjb250YWlucyBhIFRQNTMgaG90c3BvdCBTTlYgbXV0YXRpb24uIChDYW5jZXIgaG90c3BvdCBkYXRhYmFzZSBhbmQgZG93bmxvYWRhYmxlIGZpbGUgYXZhaWxhYmxlKS4gUGxlYXNlIGFsc28gY3Jvc3NjaGVjayB0aGF0IGFsbCBtdXRhdGlvbnMgZnJvbSB0aGlzIHRhYmxlIGFyZSBpbmNsdWRlZC4KIC0gQSBzYW1wbGUgY29udGFpbnMgdHdvIFRQNTMgYWx0ZXJhdGlvbnMsIHN1Z2dlc3RpbmcgKGJ1dCBub3QgY29uZmlybWluZykgdGhhdCBib3RoIGFsbGVsZXMgYXJlIGFmZmVjdGVkIChTTlYrU05WLCAoQ05WIG9yIFNWKSArIFNOVikuCiAtIEEgc2FtcGxlIGNvbnRhaW5zIG9uZSBhbHRlcmF0aW9uIChTTlYgb3IgKENOViBvciBTVikpICsgaGFzIGNhbmNlcl9wcmVkaXNwb3NpdGlvbnMgPT0gIkxpLUZyYXVtZW5pIHN5bmRyb21lIiwgc3VnZ2VzdGluZyB0aGVyZSBpcyBhIGdlcm1saW5lIHZhcmlhbnQgaW4gYWRkaXRpb24gdG8gdGhlIHNvbWF0aWMgdmFyaWFudCB3ZSBvYnNlcnZlLgogLSBBIHNhbXBsZSBkb2VzIG5vdCBoYXZlIGEgVFA1MyBhbHRlcmF0aW9ucywgYnV0IGhhcyBjYW5jZXJfcHJlZGlzcG9zaXRpb25zID09ICJMaS1GcmF1bWVuaSBzeW5kcm9tZSIgYW5kIFRQNTMgY2xhc3NpZmllciBzY29yZSBmb3IgbWF0Y2hlZCBSTkEtU2VxID4gMC41IChvciBoaWdoZXIgY3V0b2ZmIHdlIGRlY2lkZSB1cG9uIGxhdGVyKS4KIAogTm90ZTogQ05WIGFuZCBTViB3aWxsIGJlIGNvbnNpZGVyZWQgYXMgdGhlIHNhbWUgZXZlbnQsIGJ1dCB3ZSB3aWxsIGJlIGFkZGluZyBTVl9jb3VudHMgYW5kIFNWLnR5cGUgdG8gdGhlIGFsdGVyZWQgc3RhdHVzIG91dHB1dCBmaWxlCiAKKipUUDUzIGFsdGVyZWQgLSBhY3RpdmF0ZWQqKiwgaWY6CgotIEEgc2FtcGxlIGNvbnRhaW5zIG9uZSBvZiB0aGUgdHdvIFRQNTMgYWN0aXZhdGluZyBtdXRhdGlvbnMgcC5SMjczQyBhbmQgcC5SMjQ4Vy4gW0Bkb2k6MTAuMTAzOC9uZzA1OTMtNDJdCgojIyBTZXR1cApgYGB7cn0KbGlicmFyeSgiZ2dwdWJyIikKbGlicmFyeSgiZ2d0aGVtZXMiKQpsaWJyYXJ5KCJ0aWR5dmVyc2UiKQpsaWJyYXJ5KCJnZ2ZvcnRpZnkiKQpsaWJyYXJ5KCJicm9vbSIpCgojIHJvb3RkaXIKcm9vdF9kaXIgPC0gcnByb2pyb290OjpmaW5kX3Jvb3QocnByb2pyb290OjpoYXNfZGlyKCIuZ2l0IikpCmRhdGFfZGlyIDwtIGZpbGUucGF0aChyb290X2RpciwgImRhdGEiKQoKaW5wdXRfZGlyIDwtIGZpbGUucGF0aChyb290X2RpciwKICAgICAgICAgICAgICAgICAgICAgICAgICJhbmFseXNlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAidHA1M19uZjFfc2NvcmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgImlucHV0IikKIyBjZWxsIGNvbXBvc2l0aW9uCmNlbGxfbGluZV9jb21wb3NpdGlvbiA8LSByZWFkX3RzdihmaWxlLnBhdGgoIi4uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibW9sZWN1bGFyLXN1YnR5cGluZy1IR0ciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJpbnB1dCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNlbGwtbGluZS1jb21wb3NpdGlvbi50c3YiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkcjo6Y29scyggYWxpcXVvdF9pZCAgPSByZWFkcjo6Y29sX2NoYXJhY3RlcigpKSkgCgojIGhpc3RvbG9neQppZiAoIHBhcmFtcyRiYXNlX3J1biA9PTAgKXsKICBjbGluaWNhbDwtcmVhZC5kZWxpbShmaWxlLnBhdGgoZGF0YV9kaXIsImhpc3RvbG9naWVzLnRzdiIpLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCn0gZWxzZXsKICBjbGluaWNhbDwtcmVhZC5kZWxpbShmaWxlLnBhdGgoZGF0YV9kaXIsImhpc3RvbG9naWVzLWJhc2UudHN2IiksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgIAp9CgojIHJlY29yZGluZyBvZiAiQlNfRVRDOFIwVEQiIEdNS0YgTkJMIHNhbXBsZTsgbmVlZHMgdG8gYmUgcmVtb3ZlIG9uY2UgdXBkYXRlZCBpbiB2MTIKI2NsaW5pY2FsJFJOQV9saWJyYXJ5W2NsaW5pY2FsJEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQgPT0gIkJTX0VUQzhSMFREIl0gPC0gInN0cmFuZGVkIgoKaGlzdG9sb2d5IDwtIGNsaW5pY2FsICU+JQogIGRwbHlyOjpzZWxlY3QoIktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQiLAogICAgICAgICJzYW1wbGVfaWQiLAogICAgICAgICJLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEIiwKICAgICAgICAiY2FuY2VyX3ByZWRpc3Bvc2l0aW9ucyIsCiAgICAgICAgInNhbXBsZV90eXBlIiwKICAgICAgICAiZXhwZXJpbWVudGFsX3N0cmF0ZWd5IiwKICAgICAgICAiY29tcG9zaXRpb24iLAogICAgICAgICJjb2hvcnQiKSAlPiUKICAgIyBtZXJnZSBjZWxsIGxpbmUgY29tcG9zaXRpb24KICBsZWZ0X2pvaW4oY2VsbF9saW5lX2NvbXBvc2l0aW9uKQogCgojIENhbmNlciBkYXRhYmFzZQpob3RzcG90X2RhdGFiYXNlXzIwMTdfdHA1M19zbnYgPC0gcmVhZHhsOjpyZWFkX3hscyhmaWxlLnBhdGgoaW5wdXRfZGlyLCJob3RzcG90c192Mi54bHMiKSxzaGVldCA9IDEpICU+JQogIGZpbHRlcihIdWdvX1N5bWJvbCA9PSAiVFA1MyIpCmhvdHNwb3RfZGF0YWJhc2VfMjAxN190cDUzX2luZGVsIDwtIHJlYWR4bDo6cmVhZF94bHMoZmlsZS5wYXRoKGlucHV0X2RpciwiaG90c3BvdHNfdjIueGxzIiksc2hlZXQgPSAyKSAlPiUKICBmaWx0ZXIoSHVnb19TeW1ib2wgPT0gIlRQNTMiKQoKIyBwNTMsIHA2MyBhbmQgcDczIGZ1bmN0aW9uYWwgc2l0ZXMKZnVuY3Rpb25hbF9zaXRlc190cDUzIDwtIHJlYWRfdHN2KGZpbGUucGF0aChpbnB1dF9kaXIsImhvdHNwb3RfQ2hlbl8yMDA2LnRzdiIpKSAKCnJlc3VsdHNfZGlyIDwtIGZpbGUucGF0aChyb290X2RpciwKICAgICAgICAgICAgICAgICAgICAgICAgICJhbmFseXNlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAidHA1M19uZjFfc2NvcmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgInJlc3VsdHMiKQoKaWYgKCFkaXIuZXhpc3RzKHJlc3VsdHNfZGlyKSkgewogIGRpci5jcmVhdGUocmVzdWx0c19kaXIpCn0KCiMgUmVhZCBpbiBjb21iaW5lZCBzY29yZXMgZnJvbSB0cDUzLW5mMS1jbGFzc2lmaWVyIApjbGFzc2lmaWVyX3Njb3JlIDwtIHJlYWRfdHN2KGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNvbWJpbmVkX3Njb3Jlcy50c3YiKSkgJT4lCiAgZHBseXI6OnJlbmFtZShLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEX1JOQSA9IHNhbXBsZV9pZCkgJT4lCiAgZmlsdGVyKEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSURfUk5BICVpbiUgaGlzdG9sb2d5JEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQpICU+JQogIGxlZnRfam9pbihoaXN0b2xvZ3ksIGJ5PWMoIktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSURfUk5BIj0iS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIpKQoKIyBDb3B5IG51bWJlciBwZXIgc2FtcGxlIAojIG92ZXJsYXBwaW5nIGZ1bmN0aW9uYWwgZG9tYWluCmNudl9kb21haW5fb3ZlcmxhcCA8LSByZWFkX3RzdigKICBmaWxlLnBhdGgoCiAgICByZXN1bHRzX2RpciwKICAgICJsb3NzX292ZXJsYXBfZG9tYWluc190cDUzLnRzdiIpKQoKIyBTdHJ1Y3R1cmFsIHZhcmlhbnQgb3ZlcmxhcHBpbmcgCiMgb3Igd2l0aGluIGdlbmUgbG9jdXMgb2YgVFA1Mwpzdl9vdmVybGFwIDwtIHJlYWRfdHN2KAogIGZpbGUucGF0aCgKICAgIHJlc3VsdHNfZGlyLAogICAgInN2X292ZXJsYXBfdHA1My50c3YiKSkKCiMgU3RydWN0dXJhbCB2YXJpYW50IGFzIGEgZnVzaW9uIGluIGV4b24xL2ludHJvbjEKZnVzaW9uX292ZXJsYXAgPC0gcmVhZF90c3YoCiAgZmlsZS5wYXRoKAogICAgcmVzdWx0c19kaXIsCiAgICAiZnVzaW9uX2JrX3RwNTNfbG9zcy50c3YiKSkKCmBgYAoKIyMjIENoZWNrIGlmIGFsbCBmdW5jdGlvbiBzaXRlcyBhcmUgaW4gaG90c3BvdHMKCkFsbCBmdW5jdGlvbmFsIHNpdGVzIGFyZSBqdXN0IDEgYmFzZSBzbyBjaGVja2luZyBpbiBgaG90c3BvdF9kYXRhYmFzZV8yMDE3X3NudmAKCmBgYHtyfQoKZnVuY3Rpb25hbF9zaXRlc190cDUzICU+JQogIGZpbHRlcighZ3N1YigiW0EtWnxhLXpdIiwiIixwNTMpICVpbiUgaG90c3BvdF9kYXRhYmFzZV8yMDE3X3RwNTNfc252JEFtaW5vX0FjaWRfUG9zaXRpb24gKQoKYGBgCjIgZnVuY3Rpb25hbCBzaXRlcyBhcmUgbWlzc2luZyBpbiBob3RzcG90cyBkYXRhYmFzZS4KCiMjIFNOViBmb3IgVFA1MwoKUmVtb3ZpbmcgU2lsZW50IG9yIEludHJvbiBjbGFzc2lmaWVkIHZhcmlhbnRzIHRvIGNhcHR1cmUgb25seSBwdXRhdGl2ZSBkYW1hZ2luZyBtdXRhdGlvbnMKCmBgYHtyfQpjb25zZW5zdXNfdHA1M19zbnZfaW5kZWwgPC0gZGF0YS50YWJsZTo6ZnJlYWQoCiAgZmlsZS5wYXRoKGRhdGFfZGlyLCJzbnYtY29uc2Vuc3VzLXBsdXMtaG90c3BvdHMubWFmLnRzdi5neiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCA9IGMoIkNocm9tb3NvbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlN0YXJ0X1Bvc2l0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFbmRfUG9zaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlN0cmFuZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVmFyaWFudF9DbGFzc2lmaWNhdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHVtb3JfU2FtcGxlX0JhcmNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkh1Z29fU3ltYm9sIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJIR1ZTcF9TaG9ydCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEudGFibGUgPSBGQUxTRSkgJT4lCiAgZmlsdGVyKEh1Z29fU3ltYm9sID09ICJUUDUzIikgJT4lCiAgZmlsdGVyKCEoVmFyaWFudF9DbGFzc2lmaWNhdGlvbiAlaW4lIGMoIlNpbGVudCIsICJJbnRyb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgcmVtb3ZlIG90aGVyIG5vbi1hbWlubyBhY2lkIFNOVnMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMydGbGFuayIgLCI1J0ZsYW5rIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMydVVFIiLCAiNSdVVFIiICApKSkKYGBgCgojIyMgIEdhdGhlciBhbm5vdGF0aW9uIGZvciBTTlYgCgpob3RzcG90cyA6IG92ZXJsYXBzIEFBIHBvc2l0aW9uIHdoaWNoIGFyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IFNOVi9JbmRlbHMKYWN0aXZhdGluZyA6IG92ZXJsYXBzIEFBIHBvc2l0aW9uIHdoaWNoIGFyZSBmb3VuZCB0byBhY3QgYXMgZ2Fpbi1vZi1mdW5jdGlvbiBhY2NvcmRpbmcgdG8gbGl0ZXJhdHVyZQoKYGBge3J9Cgpjb25zZW5zdXNfdHA1M19zbnZfaW5kZWwgPC0gY29uc2Vuc3VzX3RwNTNfc252X2luZGVsICU+JQogIG11dGF0ZSgKICAgIGhvdHNwb3QgPSBjYXNlX3doZW4oCiAgICAgICMgc3RyaXAgUkVGIGFuZCBWYXJpYW50IEFBIHRvIGdldCBBbWlub19BY2lkX1Bvc2l0aW9uIGluIGNvbnNlbnN1cyBtYWYKICAgICAgKGdzdWIoIltBLVp8YS16XXxbLl0iLCIiLEhHVlNwX1Nob3J0KSAlaW4lIAogICAgICAgICAjIGlmIG92ZXJsYXBzIHRoZSBob3RzcG90IEFtaW5vX0FjaWRfUG9zaXRpb24gCiAgICAgICAgIGhvdHNwb3RfZGF0YWJhc2VfMjAxN190cDUzX3NudiRBbWlub19BY2lkX1Bvc2l0aW9uKSAgfiAxLAogICAgICBUUlVFIH4gMCksCiAgICBhY3RpdmF0aW5nID0gY2FzZV93aGVuKAogICAgICAjIHN0cmlwIFJFRiBhbmQgVmFyaWFudCBBQSB0byBnZXQgQW1pbm9fQWNpZF9Qb3NpdGlvbiBpbiBjb25zZW5zdXMgbWFmCiAgICAgIChnc3ViKCJbQS1afGEtel18Wy5dIiwiIixIR1ZTcF9TaG9ydCkgJWluJSAKICAgICAgICAgIyBpZiBvdmVybGFwcyB0aGUgYWN0aXZhdGluZyAgQW1pbm9fQWNpZF9Qb3NpdGlvbiAKICAgICAgICAgYygiMjczIiwiMjQ4IikpICB+IDEsCiAgICAgIFRSVUUgfiAwCiAgICApCiAgKQoKYGBgCgojIyBHYXRoZXIgU05WLENOViwgY2xhc3NpZmllciBhbmQgY2FuY2VyX3ByZWRpc3Bvc2l0aW9uIHZhbHVlcwoKV2Ugd2lsbCBnYXRoZXIgdmFsdWVzIHBlciBzYW1wbGVfaWQgc2luY2UgRE5BIGFuZCBSTkEgc2FtcGxlcyBjYW4gYmUgb25seSBtYXRjaGVkIGJ5IGBzYW1wbGVfaWRgCgoKYGBge3J9Cgp0cDUzX2FsdGVyYXRpb25zIDwtIGhpc3RvbG9neSAlPiUKICBmaWx0ZXIoZXhwZXJpbWVudGFsX3N0cmF0ZWd5ICE9ICJSTkEtU2VxIikgJT4lCiAgIyBmaWx0ZXIgZm9yIFR1bW9ycwogIGZpbHRlcihzYW1wbGVfdHlwZT09IlR1bW9yIikgJT4lCiAgIyBqb2luIGNvbnNlbnN1cyBjYWxscyBvdmVybGFwcGluZyBob3RzcG90cwogIGxlZnRfam9pbihjb25zZW5zdXNfdHA1M19zbnZfaW5kZWwsIAogICAgICAgICAgICBieT1jKCJLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEIj0iVHVtb3JfU2FtcGxlX0JhcmNvZGUiKSkgJT4lCiAgIyBqb2luIGZpbHRlcmVkIGNudiBsb3NzZXMKICBsZWZ0X2pvaW4oY252X2RvbWFpbl9vdmVybGFwLGJ5PWMoIktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQiPSJiaW9zcGVjaW1lbl9pZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzYW1wbGVfaWQiLCAiS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY29tcG9zaXRpb24iKSkgJT4lCiAgIyBqb2luIFNWIAogIGxlZnRfam9pbihzdl9vdmVybGFwLGJ5PWMoIktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQiPSJLaWRzLkZpcnN0LkJpb3NwZWNpbWVuLklELlR1bW9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzYW1wbGVfaWQiKSkgJT4lCiAgZHBseXI6OnJlbmFtZShLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEX0ROQSA9IEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQpICU+JQogICMgam9pbiBmdXNpb24KICBsZWZ0X2pvaW4oZnVzaW9uX292ZXJsYXAsYnk9InNhbXBsZV9pZCIpIAoKdHA1M19hbHRlcmF0aW9uc19zY29yZSA8LSB0cDUzX2FsdGVyYXRpb25zICU+JQogICMgYWRkIGNsYXNzaWZpZXIgc2NvcmUKICBmdWxsX2pvaW4oY2xhc3NpZmllcl9zY29yZSxieT1jKCJzYW1wbGVfaWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvbXBvc2l0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjZWxsX2xpbmVfY29tcG9zaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNhbmNlcl9wcmVkaXNwb3NpdGlvbnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNhbXBsZV90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjb2hvcnQiKSkgJT4lCiAgIyBzZWxlY3QgdXNlZnVsIGNvbHVtbnMKICBkcGx5cjo6c2VsZWN0KHNhbXBsZV9pZCxLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEX1JOQSxLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEX0ROQSwKICAgICAgICAgdHA1M19zY29yZSxjYW5jZXJfcHJlZGlzcG9zaXRpb25zLEhHVlNwX1Nob3J0LGNvcHlfbnVtYmVyLFNWX3R5cGUsRnVzaW9uTmFtZSxob3RzcG90LGFjdGl2YXRpbmcpICU+JQogIGFycmFuZ2Uoc2FtcGxlX2lkKSAlPiUKICB1bmlxdWUoKSAlPiUKICByZXBsYWNlX25hKGxpc3QoaG90c3BvdCA9IDAsIGFjdGl2YXRpbmcgPSAwKSkKCnRwNTNfYWx0ZXJhdGlvbnNfc2NvcmUgCgpgYGAKCkNvbnZlcnQgdGhlIGFib3ZlIHRvIHdpZGUgZm9ybWF0CgpgYGB7cn0KCnRwNTNfYWx0ZXJhdGlvbnNfc2NvcmVfd2lkZSA8LSB0cDUzX2FsdGVyYXRpb25zX3Njb3JlICU+JQogICMgZ3JvdXAgCiAgZ3JvdXBfYnkoc2FtcGxlX2lkLAogICAgICAgICAgIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSURfRE5BLAogICAgICAgICAgIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSURfUk5BLAogICAgICAgICAgIGNhbmNlcl9wcmVkaXNwb3NpdGlvbnMsCiAgICAgICAgICAgdHA1M19zY29yZSkgJT4lCiAgIyAKICAjIHNhbXBsZV9pZCBhcyByb3dzIGFkZCBTTlYgY291bnRzLENOViBsb3NzIGNvdW50cyAKICAjIGFuZCBob3RzcG90L2FjdGl2YXRpbmcgbXV0YXRpb24gYW5ub3RhdGlvbiAgCiAgc3VtbWFyaXNlKAogICAgIyBzdW1tYXJpemUgbGVuZ3RoIG9mIFNOViBhbmQgQ05WIGFsdGVyYXRpb25zIAogICBTTlZfaW5kZWxfY291bnRzPSBsZW5ndGgodW5pcXVlKEhHVlNwX1Nob3J0WyFpcy5uYShIR1ZTcF9TaG9ydCldKSksIAogICAgQ05WX2xvc3NfY291bnRzID0gbGVuZ3RoKHVuaXF1ZShjb3B5X251bWJlclshaXMubmEoY29weV9udW1iZXIpXSkpLAogICAgU1ZfY291bnRzID0gbGVuZ3RoKHVuaXF1ZShTVl90eXBlWyFpcy5uYShTVl90eXBlKV0pKSwKICAgIEZ1c2lvbl9jb3VudHMgPSBsZW5ndGgodW5pcXVlKEZ1c2lvbk5hbWVbIWlzLm5hKEZ1c2lvbk5hbWUpXSkpLAogICAgSEdWU3BfU2hvcnQgPSB0b1N0cmluZyh1bmlxdWUoSEdWU3BfU2hvcnQpKSwKICAgIENOVl9sb3NzX2V2aWRlbmNlID0gdG9TdHJpbmcodW5pcXVlKGNvcHlfbnVtYmVyKSksCiAgICBTVl90eXBlID0gdG9TdHJpbmcodW5pcXVlKFNWX3R5cGUpKSwKICAgIEZ1c2lvbl9ldmlkZW5jZSA9IHRvU3RyaW5nKHVuaXF1ZShGdXNpb25OYW1lKSksCiAgICAjIHN1bW1hcml6ZSB1bmlxdWUgaG90c3BvdCB2YWx1ZXMgcGVyIHNhbXBsZV9pZAogICAgaG90c3BvdCA9IG1heCh1bmlxdWUoaG90c3BvdFshaXMubmEoaG90c3BvdCkgXSkpLAogICAgYWN0aXZhdGluZyA9IG1heCh1bmlxdWUoYWN0aXZhdGluZ1shaXMubmEoYWN0aXZhdGluZyldKSkpIAoKdHA1M19hbHRlcmF0aW9uc19zY29yZV93aWRlCgpgYGAKCiMjIyBBZGQgYW5ub3RhdGlvbgoKQXMgZGlzY3Vzc2VkIGFib3ZlIHdlIHdhbnQgdG8gYW5ub3RhdGUgVFA1MyBtdXRhbnRzIHRoYXQgYXJlIHB1dGF0aXZlIGxvc3Mtb2YtZnVuY3Rpb24gT1IgIGdhaW4tb2YtZnVuY3Rpb24uCgpgYGB7cn0KCnRwNTNfYWx0ZXJhdGlvbnNfc2NvcmVfd2lkZSA8LSB0cDUzX2FsdGVyYXRpb25zX3Njb3JlX3dpZGUgJT4lCiAgbXV0YXRlKAogICAgIyBhZGQgdHA1M19hbHRlcmVkIGFubm90YXRpb24gY29sdW1uCiAgICB0cDUzX2FsdGVyZWQgPSAKICAgICAgY2FzZV93aGVuKAogICAgICAgICMgd2hlbiBhY3RpdmF0aW5nID09IDAKICAgICAgICBhY3RpdmF0aW5nID09IDAgJgogICAgICAgICAgIyBjaGVjayBpZiBtdXRhdGVkIHZhcmlhbnQgQUEgcG9zaXRpb24gb3ZlcmxhcHMgaG90c3BvdCBkYXRhYmFzZSAKICAgICAgICAgICggaG90c3BvdCA9PSAxICB8CiAgICAgICAgICAgICAgIyBjaGVjayBpZiBzYW1wbGVfaWQgaGFzIFNOVisoQ05WfFNWKSBtdXRhdGlvbgogICAgICAgICAgICAgICMgc3VnZ2VzdGluZyBib3RoIGFsbGVsZXMgYXJlIG11dGF0ZWQKICAgICAgICAgICAgICAoU05WX2luZGVsX2NvdW50cyA+PSAxICYgCiAgICAgICAgICAgICAgICAgKENOVl9sb3NzX2NvdW50cyA+PTEgfCBTVl9jb3VudHMgPj0xKSApIHwKICAgICAgICAgICAgICAjIGNoZWNrIGlmIG1vcmUgdGhhbiAxIFNOViBpcyBwcmVzZW50CiAgICAgICAgICAgICAgIyBzdWdnZXN0aW5nIGJvdGggYWxsZWxlcyBhcmUgbXV0YXRlZAogICAgICAgICAgICAgIFNOVl9pbmRlbF9jb3VudHMgPiAxIHwKICAgICAgICAgICAgICAjIGNoZWNrIGlmIFNOViBtdXRhbnQgYW5kIGhhcwogICAgICAgICAgICAgICMgTGktRnJhdW1lbmkgc3luZHJvbWUgc3VnZ2VzdGluZwogICAgICAgICAgICAgICMgZ2VybWxpbmUgVFA1MyBtdXRhbnQgYXMgY2FuY2VyIHByZWRpc3Bvc2l0aW9uIAogICAgICAgICAgICAgIChTTlZfaW5kZWxfY291bnRzID49IDEgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiTGktRnJhdW1lbmkgc3luZHJvbWUiLGNhbmNlcl9wcmVkaXNwb3NpdGlvbnMpKSB8CiAgICAgICAgICAgICAgIyBjaGVjayBpZiBDTlZ8U1YgbXV0YW50ICBhbmQgaGFzCiAgICAgICAgICAgICAgIyBMaS1GcmF1bWVuaSBzeW5kcm9tZSBzdWdnZXN0aW5nCiAgICAgICAgICAgICAgIyBnZXJtbGluZSBUUDUzIG11dGFudCBhcyBjYW5jZXIgcHJlZGlzcG9zaXRpb24gCiAgICAgICAgICAgICAgKChDTlZfbG9zc19jb3VudHMgPj0gMSB8IFNWX2NvdW50cyA+PTEgKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJMaS1GcmF1bWVuaSBzeW5kcm9tZSIsY2FuY2VyX3ByZWRpc3Bvc2l0aW9ucykpIHwKICAgICAgICAgICAgICAjIGNoZWNrIGlmIHRwNTMgaW5hY3RpdmF0aW5nIHNjb3JlIGZvciBSTkFfU2VxIGlzIGdyZWF0ZXIgdGhhdCAwLjUKICAgICAgICAgICAgICAjIGFuZCBoYXMgTGktRnJhdW1lbmkgc3luZHJvbWUgc3VnZ2VzdGluZwogICAgICAgICAgICAgICMgZ2VybWxpbmUgVFA1MyBtdXRhbnQgYXMgY2FuY2VyIHByZWRpc3Bvc2l0aW9uIAogICAgICAgICAgICAgICh0cDUzX3Njb3JlID4gMC41ICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoIkxpLUZyYXVtZW5pIHN5bmRyb21lIixjYW5jZXJfcHJlZGlzcG9zaXRpb25zKSkgfAogICAgICAgICAgICAgICMgY2hlY2sgaWYgdHA1MyBpbmFjdGl2YXRpbmcgc2NvcmUgZm9yIFJOQV9TZXEgaXMgZ3JlYXRlciB0aGF0IDAuNQogICAgICAgICAgICAgICMgYW5kIGhhcyAxIG9yIG1vcmUgU05WfCAoQ05WfFNWKSBsb3NzIGluIFRQNTMKICAgICAgICAgICAgICAodHA1M19zY29yZSA+IDAuNSAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIChTTlZfaW5kZWxfY291bnRzID49IDEgfCAoQ05WX2xvc3NfY291bnRzID49MSB8IFNWX2NvdW50cyA+PTEgKSkpCiAgICAgICAgICApIH4gImxvc3MiLAogICAgICAgICMgd2hlbiBhY3RpdmF0aW5nID09IDEKICAgICAgICBhY3RpdmF0aW5nID09IDEgfiAiYWN0aXZhdGVkIiwKICAgICAgICAjIGlmIG5vIGV2aWRlbmNlIHN1cHBvcnRzIFRQNTMgaW5hdGl2YXRpb24gb3IgYWN0aXZhdGlvbiAKICAgICAgICBUUlVFIH4gIk90aGVyIgogICAgICApCiAgKQoKYGBgCgojIyBFeHBsb3JlIGRpc3RyaWJ1dGlvbiBvZiB0cDUzX2FsdGVyZWQgc3RhdHVzIHZzIHRwNTMgaW5hY3RpdmF0aW9uIHNjb3JlcwoKYGBge3IsIG91dC53aWR0aD0iNTAlIn0KZ2dwbG90KHRwNTNfYWx0ZXJhdGlvbnNfc2NvcmVfd2lkZSwgYWVzKHggPSBmYWN0b3IodHA1M19hbHRlcmVkKSwgeSA9IHRwNTNfc2NvcmUpKSArCiAgZ2VvbV92aW9saW4oKSsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuNSwgd2lkdGggPSAwLjIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoKSArCiAgdGhlbWVfYncoKSArCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIHNjb3JlcyBhY3Jvc3MgdHA1MyBhbHRlcmVkIHN0YXR1cyIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDYwLCBoanVzdCA9IDEpKSsKICB4bGFiKCJ0cDUzIGFsdGVyZWQgc3RhdHVzIikgCgpgYGAKVFA1MyBtdXRhbnRzIHdpdGggZ2Fpbi1vZi1mdW5jdGlvbiAoYWN0aXZhdGluZykgbXV0YW50cyBwcm9tb3RlIHR1bW9yaWdlbmVzaXMgYWNjb3JkaW5nIHRvIGxpdGVyYXR1cmUuIFtSZWZlcmVuY2VdKGh0dHBzOi8vcHVibWVkLm5jYmkubmxtLm5paC5nb3YvMTc0MTc2MjcvKSBhbmQgW3JlZmVyZW5jZV0oaHR0cHM6Ly9wdWJtZWQubmNiaS5ubG0ubmloLmdvdi8yNDY3NzU3OS8pIGhhdmUgc2ltaWxhciBkaXN0cmlidXRpb24gb2YgY2xhc3NpZmllciBzY29yZXMgdG8gdGhhdCBvZiBwb3RlbnRpYWwgbG9zcy1vZi1mdW5jdGlvbiAobXVsdGktYWxsZWxpYykgVHA1MyBtdXRhdGlvbnMuIAoKIk90aGVyIiBhbm5vdGF0ZWQgc2FtcGxlcyBlaXRoZXIgZG9uJ3QgaGF2ZSBhbnkgaGlnaC1jb25maWRlbmNlIGxvc3MvZ2FpbiBUUDUzIFNOVnMgbm9yIENOViBsb3NzZXMgT1IgRE5BIHNhbXBsZSBpcyBub3QgYXZhaWxhYmxlLgoKCiMjIyBFeHByZXNzaW9uIHByb2ZpbGUgZm9yIGFjdGl2YXRpbmcgdnMgbG9zcyBUUDUzIHN0YXR1cwpgYGB7cn0KCmV4cHJfY29tYmluZWQgPC0gcmVhZFJEUyhmaWxlLnBhdGgoZGF0YV9kaXIsImdlbmUtY291bnRzLXJzZW0tZXhwZWN0ZWRfY291bnQtY29sbGFwc2VkLnJkcyIpKQoKYGBgCgojIyMjIEV4cHJlc3Npb24gcHJvZmlsZSBmb3IgYWN0aXZhdGluZyB2cyBsb3NzIFRQNTMgc3RhdHVzCgpgYGB7ciwgb3V0LndpZHRoPSI1MCUifQojIGl0ZXJhdGUgdGhyb3VnaCBhbGwgcG9zc2libGUgUk5BIGxpYnJhcnkgdHlwZSAKIyBmaXJzdCBmaWx0ZXIgdG8gCmNsaW5pY2FsX2ZpbHRlcmVkIDwtIGNsaW5pY2FsICU+JSAKICBmaWx0ZXIoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCAlaW4lIG5hbWVzKGV4cHJfY29tYmluZWQpKQoKIyBnZW5lcmF0ZSBsaXN0IG9mIGFsbCBSTkEgbGlicmFyeSB0eXBlCnJuYV9saWJyYXJ5X2xpc3QgPC0gY2xpbmljYWxfZmlsdGVyZWQgJT4lIAogIGZpbHRlcihLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEICVpbiUgbmFtZXMoZXhwcl9jb21iaW5lZCkpICU+JQogIHB1bGwoUk5BX2xpYnJhcnkpICU+JSAKICB1bmlxdWUoKQoKZm9yKGkgaW4gMTpsZW5ndGgocm5hX2xpYnJhcnlfbGlzdCkpIHsKICAKICBsaWJyYXJ5X2VhY2ggPC0gcm5hX2xpYnJhcnlfbGlzdFtbaV1dCiAgIyBwdWxsIEJTIElEcyBmb3IgdGhhdCBSTkEgbGlicmFyeSB0eXBlCiAgbGlicmFyeV9zYW1wbGVzIDwtIGNsaW5pY2FsX2ZpbHRlcmVkICU+JSAKICAgIGZpbHRlcihSTkFfbGlicmFyeSA9PSBsaWJyYXJ5X2VhY2gpICU+JQogICAgcHVsbChLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEKSAlPiUgCiAgICB1bmlxdWUoKQogICMgc3Vic2V0IGV4cHJlc3Npb24gbWF0cml4IHVzaW5nIHRoYXQKICBleHByX2VhY2ggPC0gZXhwcl9jb21iaW5lZCAlPiUgCiAgICBkcGx5cjo6c2VsZWN0KGxpYnJhcnlfc2FtcGxlcykKCiAgIyBzdWJzZXQgdG8gVFA1MwogIHN1YnNldF9leHByX2VhY2ggPC0gdChleHByX2VhY2gpWywiVFA1MyJdICU+JSAKICAgIGFzLmRhdGEuZnJhbWUoKSAKICBjb2xuYW1lcyhzdWJzZXRfZXhwcl9lYWNoKSA8LSAiVFA1MyIKICAKICAjIGNvbWJpbmUgdGhlIFRQNTMgYWx0ZXJhdGlvbiBpbmZvcm1hdGlvbgogIHN1YnNldF9leHByX2NvbWJpbmVkIDwtIHN1YnNldF9leHByX2VhY2ggJT4lCiAgICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigpICU+JSAKICAgIGxlZnRfam9pbih0cDUzX2FsdGVyYXRpb25zX3Njb3JlX3dpZGUsYnk9Yygicm93bmFtZSI9IktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSURfUk5BIikpICU+JQogICAgZmlsdGVyKHRwNTNfYWx0ZXJlZCAlaW4lIGMoImFjdGl2YXRlZCIsImxvc3MiKSkKICAKICAjIHBsb3QgZGlzdHJpYnV0aW9uIG9mIFRQNTMgZ2VuZSBleHByZXNzaW9uIAogIHBsb3Q8LSBnZ3Bsb3Qoc3Vic2V0X2V4cHJfY29tYmluZWQsIGFlcyh4ID0gZmFjdG9yKHRwNTNfYWx0ZXJlZCksIHkgPSBUUDUzKSkgKwogICAgZ2VvbV92aW9saW4oKSsKICAgIGdlb21faml0dGVyKGFscGhhID0gMC41LCB3aWR0aCA9IDAuMikgKwogICAgc3RhdF9jb21wYXJlX21lYW5zKCkgKwogICAgdGhlbWVfYncoKSArCiAgICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgVFA1MyBleHByZXNzaW9uIGFjcm9zcyB0cDUzIGFsdGVyZWQgc3RhdHVzIikgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA2MCwgaGp1c3QgPSAxKSkrCiAgICB4bGFiKHBhc3RlMCgidHA1MyBhbHRlcmVkIHN0YXR1cyB3dGggUk5BIGxpYnJhcnkgIiwgbGlicmFyeV9lYWNoKSkKCiAgcHJpbnQocGxvdCkKfQoKCmBgYAoKCiMjIyBDaGVjayBpZiBvdGhlciBjYW5jZXIgcHJlZGlzcG9zaXRpb24gaGF2ZSBoaWdoIFRQNTMgaW5hY3RpdmF0aW9uIHNjb3JlcyAgCgpgYGB7ciAsb3V0LndpZHRoPSI1MCUifQoKZ2dwbG90KHRwNTNfYWx0ZXJhdGlvbnNfc2NvcmVfd2lkZSwgYWVzKHggPSBmYWN0b3IodHA1M19hbHRlcmVkKSwgeSA9IHRwNTNfc2NvcmUpKSArCiAgZ2VvbV92aW9saW4oKSsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuNSwgd2lkdGggPSAwLjIpICsKICB0aGVtZV9idygpICsKICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2Ygc2NvcmVzIGFjcm9zcyB0cDUzIGFsdGVyZWQgc3RhdHVzIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNjAsIGhqdXN0ID0gMSkpKwogIHhsYWIoInRwNTMgYWx0ZXJlZCBzdGF0dXMiKSArCiAgZmFjZXRfd3JhcCgufmNhbmNlcl9wcmVkaXNwb3NpdGlvbnMpCgpgYGAKU29tZSBORi0xIGFuZC9vciBPdGhlciBpbmhlcml0ZWQgY29uZGl0aW9ucyBOT1MgaGF2ZSBoaWdoIHNjb3JpbmcgVFA1MyBtdXRhbnRzIGFzIHdlbGwuIAoKSW50ZXJlc3RpbmcgMiBicyBpZHMgYW5ub3RhdGVkIHdpdGggTGktRnJhdW1lbmkgc3luZnJvbWUgcHJlLWRpc3Bvc2l0aW9uIGhhdmUgdmVyeSBsb3cgdHA1MyBjbGFzc2lmaWVyIHNjb3Jlcy4KCmBgYHtyfQoKdHA1M19hbHRlcmF0aW9uc19zY29yZV93aWRlICU+JQogIGZpbHRlcihncmVwbCgiTGktRnJhdW1lbmkgc3luZHJvbWUiLCBjYW5jZXJfcHJlZGlzcG9zaXRpb25zKSwKICAgICAgICAgdHA1M19zY29yZSA8IDAuNSkgCiAgCmBgYAoKCiMjIFNhdmUgZmlsZSAKCkFkZGluZyBETkEgYW5kIFJOQSBiaW9zcGVjaW1lbiBpZHMgdG8gYHRwNTNfYWx0ZXJhdGlvbnNfc2NvcmVfd2lkZWAgYW5kIHNhdmluZwoKYGBge3J9Cgp0cDUzX2FsdGVyYXRpb25zX3Njb3JlX3dpZGUgJT4lIAogIHdyaXRlX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsInRwNTNfYWx0ZXJlZF9zdGF0dXMudHN2IikpCgpgYGAK